using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using System.Reflection;


using Microsoft.Xna.Framework.Graphics;
using CreamX.Framework.QuadTree;
using CreamX.Framework.SceneGraph;
using CreamX.Framework.Serialization;





namespace CreamX.Framework.Game.GameEngine
{
    /// <summary>
    /// Class used for managing Scenes 
    /// </summary>
    /// <remarks>Currently only works with 1 scene but not much to do to support more</remarks>
    public class CreamXGameEngine
    {


        internal GraphicsDeviceManager _manager;
        public List<QuadTreePositionItem<CreamXObject>> _insceneItemsToDraw = new List<QuadTreePositionItem<CreamXObject>>();
        private GraphicsDevice _graphicsDevice;
        public static CreamXGameEngine Instance;
        public GameTime GameTime=null;
        private string _settingsFile;
        private CreamXEngineSettings _engineSettings;
        private List<CreamXObject> _toDelete = new List<CreamXObject>();
        //internal CreamXTileSet _tileset;
        internal FRect __rect = new FRect();
        private float _elapsed;

        public float Elapsed
        {
            get { return _elapsed; }
            set { _elapsed = value; }
        }

        public CreamXGameEngine(GraphicsDevice device)
        {
            _graphicsDevice = device;
            Instance = this;
            SceneManager = new CreamXSceneManager();
        }

        public CreamXSceneManager SceneManager;

        public CreamXSceneData NewScene()
        {
            CreamXSceneData __d=new CreamXSceneData();
            SceneManager.SceneData.Add(__d);
            SceneManager.CurrentScene = __d;
            return __d;
        }
        
        public CreamXSceneData LoadScene(string filename)
        {
            
            CreamXSceneData __scene1 = CreamXGameEngine.Instance.NewScene();
            CreamXSceneDataDeSerializer __ser = new CreamXSceneDataDeSerializer();
            __ser.Assemblies = CreamXGame.Instance.Assemblies;
            __ser.DeSerialize(filename, __scene1);

            __scene1.InitializeScene();
            ResetScene();
            
            
         
            return __scene1;
        }

        void ResetScene()
        {
            _insceneItemsToDraw = new List<QuadTreePositionItem<CreamXObject>>();
            
            _toDelete.Clear();
        }
        
        /// <summary>
        /// Loads a scene for use with the creamx editor
        /// </summary>
        /// <param name="filename"></param>
        /// <param name="assemblies"></param>
        /// <returns></returns>
        public static CreamXSceneData LoadSceneStatic(string filename,List<Assembly> assemblies)
        {
            
            CreamXSceneData __scene1 = CreamXGameEngine.Instance.NewScene();// new CreamXSceneData();// CreamXEngine.GameEngine.CreamXGameEngine.Instance.NewScene();
            CreamXSceneDataDeSerializer __ser = new CreamXSceneDataDeSerializer();
            __ser.Assemblies = assemblies;
            __ser.DeSerialize(filename, __scene1);
            
            //__scene1.InitializeScene();
            return __scene1;
        }
        
        public CreamXEngineSettings EngineSettings
        {
            get { return _engineSettings; }
            set { _engineSettings = value; }
        }

        private bool InitGraphicsMode(int iWidth, int iHeight, bool bFullScreen)
        {
            // If we aren't using a full screen mode, the height and width of the window can
            // be set to anything equal to or smaller than the actual screen size.
            if (bFullScreen == false)
            {
                if ((iWidth <= GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width)
                && (iHeight <= GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height))
                {
                    _manager.PreferredBackBufferWidth = iWidth;
                    _manager.PreferredBackBufferHeight = iHeight;
                    _manager.IsFullScreen = bFullScreen;
                    _manager.ApplyChanges();
                    return true;
                }
            }
            else
            {
                // If we are using full screen mode, we should check to make sure that the display
                // adapter can handle the video mode we are trying to set. To do this, we will
                // iterate thorugh the display modes supported by the adapter and check them against
                // the mode we want to set.
                foreach (DisplayMode dm in GraphicsAdapter.DefaultAdapter.SupportedDisplayModes)
                {
                    // Check the width and height of each mode against the passed values
                    if ((dm.Width == iWidth) && (dm.Height == iHeight))
                    {
                        // The mode is supported, so set the buffer formats, apply changes and return
                        _manager.PreferredBackBufferWidth = iWidth;
                        _manager.PreferredBackBufferHeight = iHeight;
                        _manager.IsFullScreen = bFullScreen;
                        _manager.ApplyChanges();
                        return true;
                    }
                }
            }
            return false;
        }
        public void LoadSettings(string filename)
        {
            _settingsFile = filename;
            _engineSettings = CreamXEngineSettings.Load(filename);
            if (_engineSettings == null)
                _engineSettings = new CreamXEngineSettings();

            if (_manager != null)
            {
                _manager.IsFullScreen = _engineSettings.CurrentGraphicsSettings.IsFullScreen;



                if (!InitGraphicsMode(_engineSettings.CurrentGraphicsSettings.PreferredBackBufferWidth,
                    _engineSettings.CurrentGraphicsSettings.PreferredBackBufferHeight, _manager.IsFullScreen))
                {
                    if (!InitGraphicsMode(1024, 768, true))
                    {
                        if (!InitGraphicsMode(800, 600, true))
                        {
                            if (!InitGraphicsMode(640, 480, true))
                            {
                                throw new Exception("No Resolution Supported");
                            }
                        }
                    }
                }
                //_manager.PreferredBackBufferWidth = _engineSettings.CurrentGraphicsSettings.PreferredBackBufferWidth;
                //_manager.PreferredBackBufferHeight= _engineSettings.CurrentGraphicsSettings.PreferredBackBufferHeight;
                _manager.SynchronizeWithVerticalRetrace= _engineSettings.CurrentGraphicsSettings.VSyncOn;
            }
            if (_engineSettings.IsFixedTimeStep)
            {
                CreamXGame.Instance.IsFixedTimeStep=true; //.SynchronizeWithVerticalRetrace = _engineSettings.CurrentGraphicsSettings.VSyncOn;
                //CreamXGame.Instance.TargetElapsedTime = new TimeSpan(0, 0, 0, 0, 10);
            }
            else
            {
                CreamXGame.Instance.IsFixedTimeStep = false; //.SynchronizeWithVerticalRetrace = _engineSettings.CurrentGraphicsSettings.VSyncOn;
                
            }

            _graphicsDevice.Reset();
            
        }
        public bool Enabled = false;
        public void EngineUpdate(float gametime)
        {
            if (!Enabled)
                return;
            _elapsed = gametime;
            _toDelete.Clear();
            if (CreamXGameEngine.Instance.SceneManager.CurrentScene != null)
            {
                CreamXProfiler.StartProfiling(ProfilerName.SceneObjectsUpdate);
                for (int i = 0; i < CreamXGameEngine.Instance.SceneManager.CurrentScene.Objects.Count; i++)
                {
                    CreamXObject __obj = CreamXGameEngine.Instance.SceneManager.CurrentScene.Objects[i];
                    if (__obj.MarkForDelete)
                    {
                        _toDelete.Add(__obj);
                    }
                    else
                    {
                        if (!__obj.Template)
                        {
                            __obj.Update(gametime);
                        }
                    }
                }
                CreamXProfiler.StopProfiling(ProfilerName.SceneObjectsUpdate);
            }
            for (int i = 0; i < _toDelete.Count; i++)
            {
                CreamXGameEngine.Instance.SceneManager.CurrentScene.UnRegisterObject(_toDelete[i]);
            }

        }
        
        public void EngineRender(float gametime, SpriteBatch batch)
        {
            EngineRender(gametime, batch, false);
        }
        
        public void EngineRender(float gametime,SpriteBatch batch,bool all)
        {

            // This game has a blue background. Why? Because!
            //ScreenManager.GraphicsDevice.Clear(ClearOptions.Target,
            //                                   Color.CornflowerBlue, 0, 0);

            GetInSceneItemsToDraw();

            
            CreamXSceneData CurrentScene = CreamXGameEngine.Instance.SceneManager.CurrentScene;
            if (CurrentScene == null)
                return;
            
            //if (CurrentScene.TileGridManager != null)
            //{
            //    foreach (TileGrid __g in CurrentScene.TileGridManager.TileGrids)
            //    {
            //        __g.Draw(batch);
            //    }
            //}
            if (CreamXGameEngine.Instance.SceneManager.CurrentScene.QuadTreeEnabled)
            {
                if (!all )
                {
                    //float __scale = CurrentScene.Camera.Zoom;
                    //FRect __rect = new FRect(CurrentScene.Camera.Position,
                    //    Vector2.Add(CurrentScene.Camera.Position, CurrentScene.Camera.Size / __scale));
                    //List<QuadTreePositionItem<CreamXObject>> _list = new List<QuadTreePositionItem<CreamXObject>>();
                    //CreamXEngine.GameEngine.CreamXGameEngine.Instance.SceneManager.CurrentScene.TileSets[0].QuadTree.GetItems(
                    //    __rect, ref _list);


                    //Now draw the main lights on the scene objects
                    foreach (QuadTreePositionItem<CreamXObject> __ob in _insceneItemsToDraw)
                    {
                        CreamXObject __obj = __ob.Parent;
                        if (__obj.Visible)
                        {
                            __obj.Draw(batch);
                        }
                    }
                }
                else
                {
                    foreach (CreamXObject __obj in CreamXGameEngine.Instance.SceneManager.CurrentScene.Objects)
                    {
                        if (!__obj.Template  && __obj.Visible &&__obj.Material!=null)
                        {
                            if (IsInScreen(CreamXGameEngine.Instance.SceneManager.CurrentScene.Camera.GetRenderPosition(__obj.Position),
                                new Vector2(__obj.DisplayWidth, __obj.DisplayHeight)))
                            {
                                __obj.Draw(batch);
                            }
                            // primitiveBatch.DrawCircle(__obj.Position, __obj.DisplayWidth / 2);//ZOMG COMMENT out this line to make it look like it should do

                        }
                    }
                }
            }
            else
            {
                foreach (CreamXObject __obj in CreamXGameEngine.Instance.SceneManager.CurrentScene.Objects)
                {
                    if (!__obj.Template && __obj.Visible && __obj.Material!=null)
                    {
                        if (IsInScreen(CreamXGameEngine.Instance.SceneManager.CurrentScene.Camera.GetRenderPosition(__obj.Position),
                            new Vector2(__obj.DisplayWidth, __obj.DisplayHeight)))
                        {
                            __obj.Draw(batch);
                        }
                        // primitiveBatch.DrawCircle(__obj.Position, __obj.DisplayWidth / 2);//ZOMG COMMENT out this line to make it look like it should do

                    }
                }
            }


            //  __batch.End();

            //SpriteBatch __guibatch = new SpriteBatch(Game.CreamXGame.Instance.graphics.GraphicsDevice);
            // __guibatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Deferred, SaveStateMode.SaveState);
            //if (CreamXGame.Instance.DrawGUI)
            //{
            //    CreamXGame.Instance.DrawGUIStuff(gameTime, __batch);
            //}
            // __guibatch.End();

                 
        }
        
        public Vector2 ScreenSize
        {
            get { return new Vector2(_graphicsDevice.Viewport.Width, _graphicsDevice.Viewport.Height); }
        }
        
        public bool IsInScreen(Vector2 ObjectLocation, Vector2 ObjectSize)
        {
            if (ObjectLocation.X + ObjectSize.X >= 0 && ObjectLocation.X <= ScreenSize.X)
            {
                if (ObjectLocation.Y + ObjectSize.Y >= 0 && ObjectLocation.Y <= ScreenSize.Y)
                {
                    return true;
                }
            }
            return false;
        }
        
        private void GetInSceneItemsToDraw()
        {
            if (CurrentScene.QuadTreeEnabled)
            {

                //FRect __rect = new FRect(CurrentScene.Camera.Position,
                //  Vector2.Add(CurrentScene.Camera.Position, CurrentScene.Camera.Size / CurrentScene.Camera.Zoom));
                if (__rect.TopLeft != CurrentScene.Camera.Position || __rect.BottomRight == Vector2.Zero)
                {
                    __rect.TopLeft = CurrentScene.Camera.Position;
                    __rect.BottomRight = Vector2.Add(CurrentScene.Camera.Position, 
                        CurrentScene.Camera.Size / CurrentScene.Camera.Zoom);
                }
                _insceneItemsToDraw.Clear();

                CurrentScene.QuadTree.GetItems(__rect, ref _insceneItemsToDraw);

                _insceneItemsToDraw.Sort(delegate(QuadTreePositionItem<CreamXObject> p1, QuadTreePositionItem<CreamXObject> p2)
                              {
                                  return p1.Parent.Layer.CompareTo(p2.Parent.Layer);
                              });

            }
                
        }
        
        public CreamXSceneData CurrentScene
        {
            get { return CreamXGameEngine.Instance.SceneManager.CurrentScene; }
        }
        
    }
}
